home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / kiss-0.11 / kiss-0 / kiss / src / getline / getline.c < prev    next >
C/C++ Source or Header  |  1993-12-02  |  29KB  |  1,126 lines

  1. #ifndef lint
  2. static char     rcsid[] =
  3. "$Id: getline.c,v 3.11 1993/12/02 15:54:31 thewalt Exp thewalt $";
  4. static char    *copyright = "Copyright (C) 1991, 1992, 1993, Chris Thewalt";
  5. #endif
  6.  
  7. /*
  8.  * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
  9.  *
  10.  * Permission to use, copy, modify, and distribute this software 
  11.  * for any purpose and without fee is hereby granted, provided
  12.  * that the above copyright notices appear in all copies and that both the
  13.  * copyright notice and this permission notice appear in supporting
  14.  * documentation.  This software is provided "as is" without express or
  15.  * implied warranty.
  16.  *
  17.  * Thanks to the following people who have provided enhancements and fixes:
  18.  *   Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
  19.  *   DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
  20.  *   Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
  21.  */
  22.  
  23. #include       "getline.h"
  24. static int      gl_tab();  /* forward reference needed for gl_tab_hook */
  25.  
  26. /******************** imported interface *********************************/
  27.  
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <signal.h>
  32.  
  33. extern int      isatty();    
  34. extern void    *malloc();
  35. extern void     free();
  36.  
  37. /********************* exported interface ********************************/
  38.  
  39. char           *getline();        /* read a line of input */
  40. void            gl_setwidth();        /* specify width of screen */
  41. void            gl_histadd();        /* adds entries to hist */
  42. void        gl_strwidth();        /* to bind gl_strlen */
  43.  
  44. int         (*gl_in_hook)() = 0;
  45. int         (*gl_out_hook)() = 0;
  46. int         (*gl_tab_hook)() = gl_tab;
  47.  
  48. /******************** internal interface *********************************/
  49.  
  50. #define BUF_SIZE 1024
  51.  
  52. static int      gl_init_done = -1;    /* terminal mode flag  */
  53. static int      gl_termw = 80;        /* actual terminal width */
  54. static int      gl_scroll = 27;        /* width of EOL scrolling region */
  55. static int      gl_width = 0;        /* net size available for input */
  56. static int      gl_extent = 0;        /* how far to redraw, 0 means all */
  57. static int      gl_overwrite = 0;    /* overwrite mode */
  58. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  59. static char     gl_buf[BUF_SIZE];       /* input buffer */
  60. static char     gl_killbuf[BUF_SIZE]=""; /* killed text */
  61. static char    *gl_prompt;        /* to save the prompt string */
  62. static char     gl_intrc = 0;        /* keyboard SIGINT char */
  63. static char     gl_quitc = 0;        /* keyboard SIGQUIT char */
  64. static char     gl_suspc = 0;        /* keyboard SIGTSTP char */
  65. static char     gl_dsuspc = 0;        /* delayed SIGTSTP char */
  66. static int      gl_search_mode = 0;    /* search mode flag */
  67.  
  68. static void     gl_init();        /* prepare to edit a line */
  69. static void     gl_cleanup();        /* to undo gl_init */
  70. static void     gl_char_init();        /* get ready for no echo input */
  71. static void     gl_char_cleanup();    /* undo gl_char_init */
  72. static size_t     (*gl_strlen)() = (size_t(*)())strlen; 
  73.                     /* returns printable prompt width */
  74.  
  75. static void     gl_addchar();        /* install specified char */
  76. static void     gl_del();        /* del, either left (-1) or cur (0) */
  77. static void     gl_error();        /* write error msg and die */
  78. static void     gl_fixup();        /* fixup state variables and screen */
  79. static int      gl_getc();        /* read one char from terminal */
  80. static void     gl_kill();        /* delete to EOL */
  81. static void     gl_newline();        /* handle \n or \r */
  82. static void     gl_putc();        /* write one char to terminal */
  83. static void     gl_puts();        /* write a line to terminal */
  84. static void     gl_redraw();        /* issue \n and redraw all */
  85. static void     gl_transpose();        /* transpose two chars */
  86. static void     gl_yank();        /* yank killed text */
  87. static void     gl_word();        /* move a word */
  88.  
  89. static void     hist_init();    /* initializes hist pointers */
  90. static char    *hist_next();    /* return ptr to next item */
  91. static char    *hist_prev();    /* return ptr to prev item */
  92. static char    *hist_save();    /* makes copy of a string, without NL */
  93.  
  94. static void     search_addchar();    /* increment search string */
  95. static void     search_term();        /* reset with current contents */
  96. static void     search_back();        /* look back for current string */
  97. static void     search_forw();        /* look forw for current string */
  98.  
  99. /************************ nonportable part *********************************/
  100.  
  101. extern int      write();
  102. extern void     exit();
  103.  
  104. #ifdef unix
  105. #ifndef __unix__
  106. #define __unix__
  107. #endif /* not __unix__ */
  108. #endif /* unix */
  109.  
  110. #ifdef _IBMR2
  111. #ifndef __unix__
  112. #define __unix__
  113. #endif
  114. #endif
  115.  
  116. #ifdef __GO32__
  117. #include <pc.h>
  118. #undef MSDOS
  119. #undef __unix__
  120. #endif
  121.  
  122. #ifdef MSDOS
  123. #include <bios.h>
  124. #endif
  125.  
  126. #ifdef __unix__
  127. #ifndef __convexc__
  128. extern int      read();
  129. extern int      kill();
  130. extern int      ioctl();
  131. #endif /* not __convexc__ */
  132. #ifdef POSIX        /* use POSIX interface */
  133. #include <termios.h>
  134. struct termios  new_termios, old_termios;
  135. #else /* not POSIX */
  136. #include <sys/ioctl.h>
  137. #ifdef M_XENIX    /* does not really use bsd terminal interface */
  138. #undef TIOCSETN
  139. #endif /* M_XENIX */
  140. #ifdef TIOCSETN        /* use BSD interface */
  141. #include <sgtty.h>
  142. struct sgttyb   new_tty, old_tty;
  143. struct tchars   tch;
  144. struct ltchars  ltch;
  145. #else            /* use SYSV interface */
  146. #include <termio.h>
  147. struct termio   new_termio, old_termio;
  148. #endif /* TIOCSETN */
  149. #endif /* POSIX */
  150. #endif    /* __unix__ */
  151.  
  152. #ifdef vms
  153. #include <descrip.h>
  154. #include <ttdef.h>
  155. #include <iodef.h>
  156. #include unixio
  157.    
  158. static int   setbuff[2];             /* buffer to set terminal attributes */
  159. static short chan = -1;              /* channel to terminal */
  160. struct dsc$descriptor_s descrip;     /* VMS descriptor */
  161. #endif
  162.  
  163. static void
  164. gl_char_init()            /* turn off input echo */
  165. {
  166. #ifdef __unix__
  167. #ifdef POSIX
  168.     tcgetattr(0, &old_termios);
  169.     gl_intrc = old_termios.c_cc[VINTR];
  170.     gl_quitc = old_termios.c_cc[VQUIT];
  171. #ifdef VSUSP
  172.     gl_suspc = old_termios.c_cc[VSUSP];
  173. #endif
  174. #ifdef VDSUSP
  175.     gl_dsuspc = old_termios.c_cc[VDSUSP];
  176. #endif
  177.     new_termios = old_termios;
  178.     new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  179.     new_termios.c_iflag |= (IGNBRK|IGNPAR);
  180.     new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  181.     new_termios.c_cc[VMIN] = 1;
  182.     new_termios.c_cc[VTIME] = 0;
  183.     tcsetattr(0, TCSANOW, &new_termios);
  184. #else                /* not POSIX */
  185. #ifdef TIOCSETN            /* BSD */
  186.     ioctl(0, TIOCGETC, &tch);
  187.     ioctl(0, TIOCGLTC, <ch);
  188.     gl_intrc = tch.t_intrc;
  189.     gl_quitc = tch.t_quitc;
  190.     gl_suspc = ltch.t_suspc;
  191.     gl_dsuspc = ltch.t_dsuspc;
  192.     ioctl(0, TIOCGETP, &old_tty);
  193.     new_tty = old_tty;
  194.     new_tty.sg_flags |= RAW;
  195.     new_tty.sg_flags &= ~ECHO;
  196.     ioctl(0, TIOCSETN, &new_tty);
  197. #else                /* SYSV */
  198.     ioctl(0, TCGETA, &old_termio);
  199.     gl_intrc = old_termio.c_cc[VINTR];
  200.     gl_quitc = old_termio.c_cc[VQUIT];
  201.     new_termio = old_termio;
  202.     new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
  203.     new_termio.c_iflag |= (IGNBRK|IGNPAR);
  204.     new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
  205.     new_termio.c_cc[VMIN] = 1;
  206.     new_termio.c_cc[VTIME] = 0;
  207.     ioctl(0, TCSETA, &new_termio);
  208. #endif /* TIOCSETN */
  209. #endif /* POSIX */
  210. #endif /* __unix__ */
  211.  
  212. #ifdef vms
  213.     descrip.dsc$w_length  = strlen("tt:");
  214.     descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  215.     descrip.dsc$b_class   = DSC$K_CLASS_S;
  216.     descrip.dsc$a_pointer = "tt:";
  217.     (void)sys$assign(&descrip,&chan,0,0);
  218.     (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  219.     setbuff[1] |= TT$M_NOECHO;
  220.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  221. #endif /* vms */
  222. }
  223.  
  224. static void
  225. gl_char_cleanup()        /* undo effects of gl_char_init */
  226. {
  227. #ifdef __unix__
  228. #ifdef POSIX 
  229.     tcsetattr(0, TCSANOW, &old_termios);
  230. #else             /* not POSIX */
  231. #ifdef TIOCSETN        /* BSD */
  232.     ioctl(0, TIOCSETN, &old_tty);
  233. #else            /* SYSV */
  234.     ioctl(0, TCSETA, &old_termio);
  235. #endif /* TIOCSETN */
  236. #endif /* POSIX */
  237. #endif /* __unix__ */
  238.  
  239. #ifdef vms
  240.     setbuff[1] &= ~TT$M_NOECHO;
  241.     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  242.     sys$dassgn(chan);
  243.     chan = -1;
  244. #endif 
  245. }
  246.  
  247. #if MSDOS || __EMX__ || __GO32__
  248. int pc_keymap(c)
  249. int c;
  250. {
  251.     switch (c) {
  252.     case 72: c = 16;   /* up -> ^P */
  253.         break;
  254.     case 80: c = 14;   /* down -> ^N */
  255.         break;
  256.     case 75: c = 2;    /* left -> ^B */
  257.         break;
  258.     case 77: c = 6;    /* right -> ^F */
  259.         break;
  260.     default: c = 0;    /* make it garbage */
  261.     }
  262.     return c;
  263. }
  264. #endif /* MSDOS || __EMX__ || __GO32__ */
  265.  
  266. static int
  267. gl_getc()
  268. /* get a character without echoing it to screen */
  269. {
  270.     int             c;
  271. #ifdef __unix__
  272.     char            ch;
  273. #endif
  274.  
  275. #ifdef __unix__
  276.     while ((c = read(0, &ch, 1)) == -1) {
  277.     if (errno != EINTR)
  278.         break;
  279.     }
  280.     c = (ch <= 0)? -1 : ch;
  281. #endif    /* __unix__ */
  282. #ifdef MSDOS
  283.     c = _bios_keybrd(_NKEYBRD_READ);
  284. #endif  /* MSDOS */
  285. #ifdef __GO32__
  286.     c = getkey () ;
  287.     if (c > 255) c = pc_keymap(c & 0377);
  288. #endif /* __GO32__ */
  289. #ifdef __TURBOC__
  290.     while(!bioskey(1))
  291.     ;
  292.     c = bioskey(0);
  293. #endif
  294. #if MSDOS || __TURBOC__
  295.     if ((c & 0377) == 224) {
  296.     c = pc_keymap((c >> 8) & 0377);
  297.     } else {
  298.     c &= 0377;
  299.     }
  300. #endif /* MSDOS || __TURBOC__ */
  301. #ifdef __EMX__
  302.     c = _read_kbd(0, 1, 0);
  303.     if (c == 224 || c == 0) {
  304.         c = pc_keymap(_read_kbd(0, 1, 0));
  305.     } else {
  306.         c &= 0377;
  307.     }
  308. #endif
  309. #ifdef vms
  310.     if(chan < 0) {
  311.        c='\0';
  312.     }
  313.     (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  314.     c &= 0177;                        /* get a char */
  315. #endif
  316.     return c;
  317. }
  318.  
  319. static void
  320. gl_putc(c)
  321. int     c;
  322. {
  323.     char   ch = c;
  324.  
  325.     write(1, &ch, 1);
  326.     if (ch == '\n') {
  327.     ch = '\r';
  328.         write(1, &ch, 1);    /* RAW mode needs '\r', does not hurt */
  329.     }
  330. }
  331.  
  332. /******************** fairly portable part *********************************/
  333.  
  334. static void
  335. gl_puts(buf)
  336. char *buf;
  337. {
  338.     int len; 
  339.     
  340.     if (buf) {
  341.         len = strlen(buf);
  342.         write(1, buf, len);
  343.     }
  344. }
  345.  
  346. static void
  347. gl_error(buf)
  348. char *buf;
  349. {
  350.     int len = strlen(buf);
  351.  
  352.     gl_cleanup();
  353.     write(2, buf, len);
  354.     exit(1);
  355. }
  356.  
  357. static void
  358. gl_init()
  359. /* set up variables and terminal */
  360. {
  361.     if (gl_init_done < 0) {        /* -1 only on startup */
  362.         hist_init();
  363.     }
  364.     if (isatty(0) == 0 || isatty(1) == 0)
  365.     gl_error("\n*** Error: getline(): not interactive, use stdio.\n");
  366.     gl_char_init();
  367.     gl_init_done = 1;
  368. }
  369.  
  370. static void
  371. gl_cleanup()
  372. /* undo effects of gl_init, as necessary */
  373. {
  374.     if (gl_init_done > 0)
  375.         gl_char_cleanup();
  376.     gl_init_done = 0;
  377. }
  378.  
  379. void
  380. gl_setwidth(w)
  381. int  w;
  382. {
  383.     if (w > 20) {
  384.     gl_termw = w;
  385.     gl_scroll = w / 3;
  386.     } else {
  387.     gl_error("\n*** Error: minimum screen width is 21\n");
  388.     }
  389. }
  390.  
  391. char *
  392. getline(prompt)
  393. char *prompt;
  394. {
  395.     int             c, loc, tmp;
  396.  
  397. #ifdef __unix__
  398.     int                sig;
  399. #endif
  400.  
  401.     gl_init();    
  402.     gl_prompt = (prompt)? prompt : "";
  403.     gl_buf[0] = 0;
  404.     if (gl_in_hook)
  405.     gl_in_hook(gl_buf);
  406.     gl_fixup(gl_prompt, -2, BUF_SIZE);
  407.     while ((c = gl_getc()) >= 0) {
  408.     gl_extent = 0;      /* reset to full extent */
  409.     if (isprint(c)) {
  410.         if (gl_search_mode)
  411.            search_addchar(c);
  412.         else
  413.            gl_addchar(c);
  414.     } else {
  415.         if (gl_search_mode) {
  416.             if (c == '\033' || c == '\016' || c == '\020') {
  417.                 search_term();
  418.                 c = 0;             /* ignore the character */
  419.         } else if (c == '\010' || c == '\177') {
  420.             search_addchar(-1); /* unwind search string */
  421.             c = 0;
  422.         } else if (c != '\022' && c != '\023') {
  423.             search_term();    /* terminate and handle char */
  424.         }
  425.         }
  426.         switch (c) {
  427.           case '\n': case '\r':             /* newline */
  428.         gl_newline();
  429.         gl_cleanup();
  430.         return gl_buf;
  431.         /*NOTREACHED*/
  432.         break; 
  433.           case '\001': gl_fixup(gl_prompt, -1, 0);        /* ^A */
  434.         break;
  435.           case '\002': gl_fixup(gl_prompt, -1, gl_pos-1);    /* ^B */
  436.         break;
  437.           case '\004':                    /* ^D */
  438.         if (gl_cnt == 0) {
  439.             gl_buf[0] = 0;
  440.             gl_cleanup();
  441.             gl_putc('\n');
  442.             return gl_buf;
  443.         } else {
  444.             gl_del(0);
  445.         }
  446.         break;
  447.           case '\005': gl_fixup(gl_prompt, -1, gl_cnt);    /* ^E */
  448.         break;
  449.           case '\006': gl_fixup(gl_prompt, -1, gl_pos+1);    /* ^F */
  450.         break;
  451.           case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  452.         break;
  453.           case '\t':                        /* TAB */
  454.                 if (gl_tab_hook) {
  455.             tmp = gl_pos;
  456.                 loc = gl_tab_hook(gl_buf, gl_strlen(gl_prompt), &tmp);
  457.                 if (loc >= 0 || tmp != gl_pos)
  458.                     gl_fixup(gl_prompt, loc, tmp);
  459.                 }
  460.         break;
  461.           case '\013': gl_kill(gl_pos);            /* ^K */
  462.         break;
  463.           case '\014': gl_redraw();                /* ^L */
  464.         break;
  465.           case '\016':                     /* ^N */
  466.         strcpy(gl_buf, hist_next());
  467.                 if (gl_in_hook)
  468.                 gl_in_hook(gl_buf);
  469.         gl_fixup(gl_prompt, 0, BUF_SIZE);
  470.         break;
  471.           case '\017': gl_overwrite = !gl_overwrite;           /* ^O */
  472.         break;
  473.           case '\020':                     /* ^P */
  474.         strcpy(gl_buf, hist_prev());
  475.                 if (gl_in_hook)
  476.                 gl_in_hook(gl_buf);
  477.         gl_fixup(gl_prompt, 0, BUF_SIZE);
  478.         break;
  479.           case '\022': search_back(1);            /* ^R */
  480.         break;
  481.           case '\023': search_forw(1);            /* ^S */
  482.         break;
  483.           case '\024': gl_transpose();            /* ^T */
  484.         break;
  485.               case '\025': gl_kill(0);                /* ^U */
  486.         break;
  487.           case '\031': gl_yank();                /* ^Y */
  488.         break;
  489.           case '\033':                /* ansi arrow keys */
  490.         c = gl_getc();
  491.         if (c == '[') {
  492.             switch(c = gl_getc()) {
  493.               case 'A':                         /* up */
  494.                 strcpy(gl_buf, hist_prev());
  495.                         if (gl_in_hook)
  496.                         gl_in_hook(gl_buf);
  497.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  498.                 break;
  499.               case 'B':                             /* down */
  500.                 strcpy(gl_buf, hist_next());
  501.                         if (gl_in_hook)
  502.                         gl_in_hook(gl_buf);
  503.                 gl_fixup(gl_prompt, 0, BUF_SIZE);
  504.                 break;
  505.               case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */
  506.                 break;
  507.               case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */
  508.                 break;
  509.               default: gl_putc('\007');         /* who knows */
  510.                 break;
  511.             }
  512.         } else if (c == 'f' || c == 'F') {
  513.             gl_word(1);
  514.         } else if (c == 'b' || c == 'B') {
  515.             gl_word(-1);
  516.         } else
  517.             gl_putc('\007');
  518.         break;
  519.           default:        /* check for a terminal signal */
  520. #ifdef __unix__
  521.             if (c > 0) {    /* ignore 0 (reset above) */
  522.                 sig = 0;
  523. #ifdef SIGINT
  524.                 if (c == gl_intrc)
  525.                     sig = SIGINT;
  526. #endif
  527. #ifdef SIGQUIT
  528.                 if (c == gl_quitc)
  529.                     sig = SIGQUIT;
  530. #endif
  531. #ifdef SIGTSTP
  532.                 if (c == gl_suspc || c == gl_dsuspc)
  533.                     sig = SIGTSTP;
  534. #endif
  535.                     if (sig != 0) {
  536.                     gl_cleanup();
  537.                     kill(0, sig);
  538.                     gl_init();
  539.                     gl_redraw();
  540.             c = 0;
  541.             } 
  542.         }
  543. #endif /* __unix__ */
  544.                 if (c > 0)
  545.             gl_putc('\007');
  546.         break;
  547.         }
  548.     }
  549.     }
  550.     gl_cleanup();
  551.     gl_buf[0] = 0;
  552.     return gl_buf;
  553. }
  554.  
  555. static void
  556. gl_addchar(c)
  557. int c;
  558. /* adds the character c to the input buffer at current location */
  559. {
  560.     int  i;
  561.  
  562.     if (gl_cnt >= BUF_SIZE - 1)
  563.     gl_error("\n*** Error: getline(): input buffer overflow\n");
  564.     if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  565.         for (i=gl_cnt; i >= gl_pos; i--)
  566.             gl_buf[i+1] = gl_buf[i];
  567.         gl_buf[gl_pos] = c;
  568.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  569.     } else {
  570.     gl_buf[gl_pos] = c;
  571.     gl_extent = 1;
  572.         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
  573.     }
  574. }
  575.  
  576. static void
  577. gl_yank()
  578. /* adds the kill buffer to the input buffer at current location */
  579. {
  580.     int  i, len;
  581.  
  582.     len = strlen(gl_killbuf);
  583.     if (len > 0) {
  584.     if (gl_overwrite == 0) {
  585.             if (gl_cnt + len >= BUF_SIZE - 1) 
  586.             gl_error("\n*** Error: getline(): input buffer overflow\n");
  587.             for (i=gl_cnt; i >= gl_pos; i--)
  588.                 gl_buf[i+len] = gl_buf[i];
  589.         for (i=0; i < len; i++)
  590.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  591.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  592.     } else {
  593.         if (gl_pos + len > gl_cnt) {
  594.                 if (gl_pos + len >= BUF_SIZE - 1) 
  595.                 gl_error("\n*** Error: getline(): input buffer overflow\n");
  596.         gl_buf[gl_pos + len] = 0;
  597.             }
  598.         for (i=0; i < len; i++)
  599.                 gl_buf[gl_pos+i] = gl_killbuf[i];
  600.         gl_extent = len;
  601.             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
  602.     }
  603.     } else
  604.     gl_putc('\007');
  605. }
  606.  
  607. static void
  608. gl_transpose()
  609. /* switch character under cursor and to left of cursor */
  610. {
  611.     int    c;
  612.  
  613.     if (gl_pos > 0 && gl_cnt > gl_pos) {
  614.     c = gl_buf[gl_pos-1];
  615.     gl_buf[gl_pos-1] = gl_buf[gl_pos];
  616.     gl_buf[gl_pos] = c;
  617.     gl_extent = 2;
  618.     gl_fixup(gl_prompt, gl_pos-1, gl_pos);
  619.     } else
  620.     gl_putc('\007');
  621. }
  622.  
  623. static void
  624. gl_newline()
  625. /*
  626.  * Cleans up entire line before returning to caller. A \n is appended.
  627.  * If line longer than screen, we redraw starting at beginning
  628.  */
  629. {
  630.     int change = gl_cnt;
  631.     int len = gl_cnt;
  632.     int loc = gl_width - 5;    /* shifts line back to start position */
  633.  
  634.     if (gl_cnt >= BUF_SIZE - 1) 
  635.         gl_error("\n*** Error: getline(): input buffer overflow\n");
  636.     if (gl_out_hook) {
  637.     change = gl_out_hook(gl_buf);
  638.         len = strlen(gl_buf);
  639.     } 
  640.     if (loc > len)
  641.     loc = len;
  642.     gl_fixup(gl_prompt, change, loc);    /* must do this before appending \n */
  643.     gl_buf[len] = '\n';
  644.     gl_buf[len+1] = '\0';
  645.     gl_putc('\n');
  646. }
  647.  
  648. static void
  649. gl_del(loc)
  650. int loc;
  651. /*
  652.  * Delete a character.  The loc variable can be:
  653.  *    -1 : delete character to left of cursor
  654.  *     0 : delete character under cursor
  655.  */
  656. {
  657.     int i;
  658.  
  659.     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  660.         for (i=gl_pos+loc; i < gl_cnt; i++)
  661.         gl_buf[i] = gl_buf[i+1];
  662.     gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
  663.     } else
  664.     gl_putc('\007');
  665. }
  666.  
  667. static void
  668. gl_kill(pos)
  669. int pos;
  670. /* delete from pos to the end of line */
  671. {
  672.     if (pos < gl_cnt) {
  673.     strcpy(gl_killbuf, gl_buf + pos);
  674.     gl_buf[pos] = '\0';
  675.     gl_fixup(gl_prompt, pos, pos);
  676.     } else
  677.     gl_putc('\007');
  678. }
  679.  
  680. static void
  681. gl_word(direction)
  682. int direction;
  683. /* move forward or backword one word */
  684. {
  685.     int pos = gl_pos;
  686.  
  687.     if (direction > 0) {        /* forward */
  688.         while (!isspace(gl_buf[pos]) && pos < gl_cnt) 
  689.         pos++;
  690.     while (isspace(gl_buf[pos]) && pos < gl_cnt)
  691.         pos++;
  692.     } else {                /* backword */
  693.     if (pos > 0)
  694.         pos--;
  695.     while (isspace(gl_buf[pos]) && pos > 0)
  696.         pos--;
  697.         while (!isspace(gl_buf[pos]) && pos > 0) 
  698.         pos--;
  699.     if (pos < gl_cnt && isspace(gl_buf[pos]))   /* move onto word */
  700.         pos++;
  701.     }
  702.     gl_fixup(gl_prompt, -1, pos);
  703. }
  704.  
  705. static void
  706. gl_redraw()
  707. /* emit a newline, reset and redraw prompt and current input line */
  708. {
  709.     if (gl_init_done > 0) {
  710.         gl_putc('\n');
  711.         gl_fixup(gl_prompt, -2, gl_pos);
  712.     }
  713. }
  714.  
  715. static void
  716. gl_fixup(prompt, change, cursor)
  717. char  *prompt;
  718. int    change, cursor;
  719. /*
  720.  * This function is used both for redrawing when input changes or for
  721.  * moving within the input line.  The parameters are:
  722.  *   prompt:  compared to last_prompt[] for changes;
  723.  *   change : the index of the start of changes in the input buffer,
  724.  *            with -1 indicating no changes, -2 indicating we're on
  725.  *            a new line, redraw everything.
  726.  *   cursor : the desired location of the cursor after the call.
  727.  *            A value of BUF_SIZE can be used  to indicate the cursor should
  728.  *            move just past the end of the input line.
  729.  */
  730. {
  731.     static int   gl_shift;    /* index of first on screen character */
  732.     static int   off_right;    /* true if more text right of screen */
  733.     static int   off_left;    /* true if more text left of screen */
  734.     static char  last_prompt[80] = "";
  735.     int          left = 0, right = -1;        /* bounds for redraw */
  736.     int          pad;        /* how much to erase at end of line */
  737.     int          backup;        /* how far to backup before fixing */
  738.     int          new_shift;     /* value of shift based on cursor */
  739.     int          extra;         /* adjusts when shift (scroll) happens */
  740.     int          i;
  741.     int          new_right = -1; /* alternate right bound, using gl_extent */
  742.     int          l1, l2;
  743.  
  744.     if (change == -2) {   /* reset */
  745.     gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
  746.     gl_putc('\r');
  747.     gl_puts(prompt);
  748.     strcpy(last_prompt, prompt);
  749.     change = 0;
  750.         gl_width = gl_termw - gl_strlen(prompt);
  751.     } else if (strcmp(prompt, last_prompt) != 0) {
  752.     l1 = gl_strlen(last_prompt);
  753.     l2 = gl_strlen(prompt);
  754.     gl_cnt = gl_cnt + l1 - l2;
  755.     strcpy(last_prompt, prompt);
  756.     gl_putc('\r');
  757.     gl_puts(prompt);
  758.     gl_pos = gl_shift;
  759.         gl_width = gl_termw - l2;
  760.     change = 0;
  761.     }
  762.     pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  763.     backup = gl_pos - gl_shift;
  764.     if (change >= 0) {
  765.         gl_cnt = strlen(gl_buf);
  766.         if (change > gl_cnt)
  767.         change = gl_cnt;
  768.     }
  769.     if (cursor > gl_cnt) {
  770.     if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  771.         gl_putc('\007');
  772.     cursor = gl_cnt;
  773.     }
  774.     if (cursor < 0) {
  775.     gl_putc('\007');
  776.     cursor = 0;
  777.     }
  778.     if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
  779.     extra = 2;            /* shift the scrolling boundary */
  780.     else 
  781.     extra = 0;
  782.     new_shift = cursor + extra + gl_scroll - gl_width;
  783.     if (new_shift > 0) {
  784.     new_shift /= gl_scroll;
  785.     new_shift *= gl_scroll;
  786.     } else
  787.     new_shift = 0;
  788.     if (new_shift != gl_shift) {    /* scroll occurs */
  789.     gl_shift = new_shift;
  790.     off_left = (gl_shift)? 1 : 0;
  791.     off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  792.         left = gl_shift;
  793.     new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  794.     } else if (change >= 0) {        /* no scroll, but text changed */
  795.     if (change < gl_shift + off_left) {
  796.         left = gl_shift;
  797.     } else {
  798.         left = change;
  799.         backup = gl_pos - change;
  800.     }
  801.     off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  802.     right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  803.     new_right = (gl_extent && (right > left + gl_extent))? 
  804.                  left + gl_extent : right;
  805.     }
  806.     pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  807.     pad = (pad < 0)? 0 : pad;
  808.     if (left <= right) {        /* clean up screen */
  809.     for (i=0; i < backup; i++)
  810.         gl_putc('\b');
  811.     if (left == gl_shift && off_left) {
  812.         gl_putc('$');
  813.         left++;
  814.         }
  815.     for (i=left; i < new_right; i++)
  816.         gl_putc(gl_buf[i]);
  817.     gl_pos = new_right;
  818.     if (off_right && new_right == right) {
  819.         gl_putc('$');
  820.         gl_pos++;
  821.     } else { 
  822.         for (i=0; i < pad; i++)    /* erase remains of prev line */
  823.         gl_putc(' ');
  824.         gl_pos += pad;
  825.     }
  826.     }
  827.     i = gl_pos - cursor;        /* move to final cursor location */
  828.     if (i > 0) {
  829.     while (i--)
  830.        gl_putc('\b');
  831.     } else {
  832.     for (i=gl_pos; i < cursor; i++)
  833.         gl_putc(gl_buf[i]);
  834.     }
  835.     gl_pos = cursor;
  836. }
  837.  
  838. static int
  839. gl_tab(buf, offset, loc)
  840. char  *buf;
  841. int    offset;
  842. int   *loc;
  843. /* default tab handler, acts like tabstops every 8 cols */
  844. {
  845.     int i, count, len;
  846.  
  847.     len = strlen(buf);
  848.     count = 8 - (offset + *loc) % 8;
  849.     for (i=len; i >= *loc; i--)
  850.         buf[i+count] = buf[i];
  851.     for (i=0; i < count; i++)
  852.         buf[*loc+i] = ' ';
  853.     i = *loc;
  854.     *loc = i + count;
  855.     return i;
  856. }
  857.  
  858. /******************* strlen stuff **************************************/
  859.  
  860. void gl_strwidth(func)
  861. size_t (*func)();
  862. {
  863.     if (func != 0) {
  864.     gl_strlen = func;
  865.     }
  866. }
  867.  
  868. /******************* History stuff **************************************/
  869.  
  870. #ifndef HIST_SIZE
  871. #define HIST_SIZE 100
  872. #endif
  873.  
  874. static int      hist_pos = 0, hist_last = 0;
  875. static char    *hist_buf[HIST_SIZE];
  876.  
  877. static void
  878. hist_init()
  879. {
  880.     int i;
  881.  
  882.     hist_buf[0] = "";
  883.     for (i=1; i < HIST_SIZE; i++)
  884.     hist_buf[i] = (char *)0;
  885. }
  886.  
  887. void
  888. gl_histadd(buf)
  889. char *buf;
  890. {
  891.     static char *prev = 0;
  892.     char *p = buf;
  893.     int len;
  894.  
  895.     /* in case we call gl_histadd() before we call getline() */
  896.     if (gl_init_done < 0) {        /* -1 only on startup */
  897.         hist_init();
  898.         gl_init_done = 0;
  899.     }
  900.     while (*p == ' ' || *p == '\t' || *p == '\n') 
  901.     p++;
  902.     if (*p) {
  903.     len = strlen(buf);
  904.     if (strchr(p, '\n'))     /* previously line already has NL stripped */
  905.         len--;
  906.     if (prev == 0 || strlen(prev) != len || 
  907.                 strncmp(prev, buf, len) != 0) {
  908.             hist_buf[hist_last] = hist_save(buf);
  909.         prev = hist_buf[hist_last];
  910.             hist_last = (hist_last + 1) % HIST_SIZE;
  911.             if (hist_buf[hist_last] && *hist_buf[hist_last]) {
  912.             free(hist_buf[hist_last]);
  913.             }
  914.         hist_buf[hist_last] = "";
  915.     }
  916.     }
  917.     hist_pos = hist_last;
  918. }
  919.  
  920. static char *
  921. hist_prev()
  922. /* loads previous hist entry into input buffer, sticks on first */
  923. {
  924.     char *p = 0;
  925.     int   next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  926.  
  927.     if (hist_buf[hist_pos] != 0 && next != hist_last) {
  928.         hist_pos = next;
  929.         p = hist_buf[hist_pos];
  930.     } 
  931.     if (p == 0) {
  932.     p = "";
  933.     gl_putc('\007');
  934.     }
  935.     return p;
  936. }
  937.  
  938. static char *
  939. hist_next()
  940. /* loads next hist entry into input buffer, clears on last */
  941. {
  942.     char *p = 0;
  943.  
  944.     if (hist_pos != hist_last) {
  945.         hist_pos = (hist_pos+1) % HIST_SIZE;
  946.     p = hist_buf[hist_pos];
  947.     } 
  948.     if (p == 0) {
  949.     p = "";
  950.     gl_putc('\007');
  951.     }
  952.     return p;
  953. }
  954.  
  955. static char *
  956. hist_save(p)
  957. char *p;
  958. /* makes a copy of the string */
  959. {
  960.     char *s = 0;
  961.     int   len = strlen(p);
  962.     char *nl = strchr(p, '\n');
  963.  
  964.     if (nl) {
  965.         if ((s = malloc(len)) != 0) {
  966.             strncpy(s, p, len-1);
  967.         s[len-1] = 0;
  968.     }
  969.     } else {
  970.         if ((s = malloc(len+1)) != 0) {
  971.             strcpy(s, p);
  972.         }
  973.     }
  974.     if (s == 0) 
  975.     gl_error("\n*** Error: hist_save() failed on malloc\n");
  976.     return s;
  977. }
  978.  
  979. /******************* Search stuff **************************************/
  980.  
  981. static char  search_prompt[101];  /* prompt includes search string */
  982. static char  search_string[100];
  983. static int   search_pos = 0;      /* current location in search_string */
  984. static int   search_forw_flg = 0; /* search direction flag */
  985. static int   search_last = 0;      /* last match found */
  986.  
  987. static void  
  988. search_update(c)
  989. int c;
  990. {
  991.     if (c == 0) {
  992.     search_pos = 0;
  993.         search_string[0] = 0;
  994.         search_prompt[0] = '?';
  995.         search_prompt[1] = ' ';
  996.         search_prompt[2] = 0;
  997.     } else if (c > 0) {
  998.         search_string[search_pos] = c;
  999.         search_string[search_pos+1] = 0;
  1000.         search_prompt[search_pos] = c;
  1001.         search_prompt[search_pos+1] = '?';
  1002.         search_prompt[search_pos+2] = ' ';
  1003.         search_prompt[search_pos+3] = 0;
  1004.     search_pos++;
  1005.     } else {
  1006.     if (search_pos > 0) {
  1007.         search_pos--;
  1008.             search_string[search_pos] = 0;
  1009.             search_prompt[search_pos] = '?';
  1010.             search_prompt[search_pos+1] = ' ';
  1011.             search_prompt[search_pos+2] = 0;
  1012.     } else {
  1013.         gl_putc('\007');
  1014.         hist_pos = hist_last;
  1015.     }
  1016.     }
  1017. }
  1018.  
  1019. static void 
  1020. search_addchar(c)
  1021. int  c;
  1022. {
  1023.     char *loc;
  1024.  
  1025.     search_update(c);
  1026.     if (c < 0) {
  1027.     if (search_pos > 0) {
  1028.         hist_pos = search_last;
  1029.     } else {
  1030.         gl_buf[0] = 0;
  1031.         hist_pos = hist_last;
  1032.     }
  1033.     strcpy(gl_buf, hist_buf[hist_pos]);
  1034.     }
  1035.     if ((loc = strstr(gl_buf, search_string)) != 0) {
  1036.     gl_fixup(search_prompt, 0, loc - gl_buf);
  1037.     } else if (search_pos > 0) {
  1038.         if (search_forw_flg) {
  1039.         search_forw(0);
  1040.         } else {
  1041.         search_back(0);
  1042.         }
  1043.     } else {
  1044.     gl_fixup(search_prompt, 0, 0);
  1045.     }
  1046. }
  1047.  
  1048. static void     
  1049. search_term()
  1050. {
  1051.     gl_search_mode = 0;
  1052.     if (gl_buf[0] == 0)        /* not found, reset hist list */
  1053.         hist_pos = hist_last;
  1054.     if (gl_in_hook)
  1055.     gl_in_hook(gl_buf);
  1056.     gl_fixup(gl_prompt, 0, gl_pos);
  1057. }
  1058.  
  1059. static void     
  1060. search_back(new_search)
  1061. int new_search;
  1062. {
  1063.     int    found = 0;
  1064.     char  *p, *loc;
  1065.  
  1066.     search_forw_flg = 0;
  1067.     if (gl_search_mode == 0) {
  1068.     search_last = hist_pos = hist_last;    
  1069.     search_update(0);    
  1070.     gl_search_mode = 1;
  1071.         gl_buf[0] = 0;
  1072.     gl_fixup(search_prompt, 0, 0);
  1073.     } else if (search_pos > 0) {
  1074.     while (!found) {
  1075.         p = hist_prev();
  1076.         if (*p == 0) {        /* not found, done looking */
  1077.            gl_buf[0] = 0;
  1078.            gl_fixup(search_prompt, 0, 0);
  1079.            found = 1;
  1080.         } else if ((loc = strstr(p, search_string)) != 0) {
  1081.            strcpy(gl_buf, p);
  1082.            gl_fixup(search_prompt, 0, loc - p);
  1083.            if (new_search)
  1084.            search_last = hist_pos;
  1085.            found = 1;
  1086.         } 
  1087.     }
  1088.     } else {
  1089.         gl_putc('\007');
  1090.     }
  1091. }
  1092.  
  1093. static void     
  1094. search_forw(new_search)
  1095. int new_search;
  1096. {
  1097.     int    found = 0;
  1098.     char  *p, *loc;
  1099.  
  1100.     search_forw_flg = 1;
  1101.     if (gl_search_mode == 0) {
  1102.     search_last = hist_pos = hist_last;    
  1103.     search_update(0);    
  1104.     gl_search_mode = 1;
  1105.         gl_buf[0] = 0;
  1106.     gl_fixup(search_prompt, 0, 0);
  1107.     } else if (search_pos > 0) {
  1108.     while (!found) {
  1109.         p = hist_next();
  1110.         if (*p == 0) {        /* not found, done looking */
  1111.            gl_buf[0] = 0;
  1112.            gl_fixup(search_prompt, 0, 0);
  1113.            found = 1;
  1114.         } else if ((loc = strstr(p, search_string)) != 0) {
  1115.            strcpy(gl_buf, p);
  1116.            gl_fixup(search_prompt, 0, loc - p);
  1117.            if (new_search)
  1118.            search_last = hist_pos;
  1119.            found = 1;
  1120.         } 
  1121.     }
  1122.     } else {
  1123.         gl_putc('\007');
  1124.     }
  1125. }
  1126.